/*
 *   FILE: toplevel.c
 * AUTHOR: name (email)
 *   DATE: March 24 23:59:59 PST 2009
 *  DESCR:
 */

/* #define DEBUG */

#include "main.h"
#include <string>
#include "mazewar.h"

static bool		updateView;	/* true if update needed */
MazewarInstance::Ptr M;

/* Use this socket address to send packets to the multi-cast group. */
static Sockaddr         groupAddr;
#define MAX_OTHER_RATS  (MAX_RATS - 1)
	
MSGQueue queue;			/* Message queue */

int TimeZero;			/* Baseline time */

int main(int argc, char *argv[])
{
    char        *ratName, *mazeName;

	signal(SIGHUP, quit);
	signal(SIGINT, quit);
	signal(SIGTERM, quit);

	M = MazewarInstance::mazewarInstanceNew(string(""));
	
	/* Get user input name position etc */
	if (argc > 1) {
		strcpy(MY_NAME, argv[1]);
		if (argc > 2) 
			M->xlocIs(atoi(argv[2]));
		if (argc > 3) 
			M->ylocIs(atoi(argv[3]));
		if (argc > 4)
			switch (argv[4][0]) {
				case 'n': { M->dirIs(NORTH);break;}
				case 's': { M->dirIs(SOUTH);break;}
				case 'e': { M->dirIs(EAST);break;}
				case 'w': { M->dirIs(WEST);break;}
			}
	}

	if (strlen(MY_NAME) == 0) {
	    getName("Welcome to CS244B MazeWar!\n\nYour Name", &ratName);
		getString("Maze Name", &mazeName);
		strncpy(MY_NAME, ratName, NAMESIZE);
		MY_NAME[NAMESIZE] = '\0';
   	 	free(ratName);
	    free(mazeName);   	 	
   	}
	MazeInit(argc, argv);

	/* If user doesn't input any location, or location is invalid, random one */
	if (M->maze_[MY_X_LOC][MY_Y_LOC])
	    NewPosition(M);
	
	/* Calculate the shortest path between every pair of points on map */
	DistanceInit ();

	/* Initialize the base timeline */
	struct timeval timenow;
	gettimeofday(&timenow, 0);
	TimeZero = timenow.tv_sec * 1000 + timenow.tv_usec / 1000;
    
    /* Add regular update to message queue */
	MW244BPacket		*pack = new MW244BPacket;
	pack->messageID = 0;
	pack->type = PACKET_REG;
	MSGQueueInstance *queueInst = new MSGQueueInstance(QTYPE_REG, pack, MAX_RATS, 0);
	queue.enQueue (queueInst);	  	

	/* Main loop */
	play();

	/* Clean up */	
	queue.freeQueue(); 
	return (0);
}


void DistanceInit () {
	int maxDist = MAZEXMAX * MAZEYMAX;
	int ix, iy, jx, jy, kx, ky;
	
	/* Initialize */
	for (ix = 0; ix < MAZEXMAX; ix++)
		for (iy = 0; iy < MAZEYMAX; iy++)
			for (jx = 0; jx < MAZEXMAX; jx++)
				for (jy = 0; jy < MAZEYMAX; jy++) 
					M->dist[ix][iy][jx][jy] = maxDist;

	for (ix = 0; ix < MAZEXMAX; ix++)
		for (iy = 0; iy < MAZEYMAX; iy++)
			if (!M->maze_[ix][iy]) {			
				M->dist[ix][iy][ix][iy] = 0;
				M->dist[ix][iy][ix+1][iy] = 1;
				M->dist[ix][iy][ix-1][iy] = 1;
				M->dist[ix][iy][ix][iy+1] = 1;
				M->dist[ix][iy][ix][iy-1] = 1;		
			}
	
	/* Floyd-Warshall algorithm */
	for (kx = 0; kx < MAZEXMAX; kx++)
		for (ky = 0; ky < MAZEYMAX; ky++) 
			if (!M->maze_[kx][ky]) 
				for (ix = 0; ix < MAZEXMAX; ix++)
					for (iy = 0; iy < MAZEYMAX; iy++)
						if (!M->maze_[ix][iy])
							for (jx = 0; jx < MAZEXMAX; jx++)
								for (jy = 0; jy < MAZEYMAX; jy++)
									if (!M->maze_[jx][jy])
										if (M->dist[ix][iy][kx][ky] + M->dist[kx][ky][jx][jy] < M->dist[ix][iy][jx][jy])
											M->dist[ix][iy][jx][jy] = M->dist[ix][iy][kx][ky] + M->dist[kx][ky][jx][jy];
}

/* Generate real time update message and push into message queue. */
void realTimeUpdate () {
	/* Generate a queue message dedicated to waiting for ACK */
	MW244BPacket *msg = new MW244BPacket;
	msg->messageID = M->getMessageID ();
	msg->type = PACKET_RT;
	MSGQueueInstance *queueInst = new MSGQueueInstance (QTYPE_ACK, msg, MAX_RATS, 0);
	queue.enQueueAll(queueInst);
}

/* ----------------------------------------------------------------------- */

void
play(void)
{
	MWEvent		event;
	MW244BPacket	incoming;

	event.eventDetail = &incoming;

	while (TRUE) {
		NextEvent(&event, M->theSocket());
		if (!M->peeking())
			switch(event.eventType) {
			case EVENT_A:
				aboutFace();
				realTimeUpdate ();
				break;

			case EVENT_S:
				leftTurn();
				realTimeUpdate ();				
				break;

			case EVENT_D:
				forward();
				realTimeUpdate ();				
				break;

			case EVENT_F:
				rightTurn();
				realTimeUpdate ();				
				break;

			case EVENT_BAR:
				backward();
				realTimeUpdate ();				
				break;

			case EVENT_LEFT_D:
				peekLeft();
				break;

			case EVENT_MIDDLE_D:
				shoot();
				break;

			case EVENT_RIGHT_D:
				peekRight();
				break;

			case EVENT_NETWORK:
				processPacket(&event);
				break;

			case EVENT_INT:
				quit(0);
				break;

			}
		else
			switch (event.eventType) {
			case EVENT_RIGHT_U:
			case EVENT_LEFT_U:
				peekStop();
				break;

			case EVENT_NETWORK:
				processPacket(&event);
				break;
			}

   		
   		/* Update rat states */
		ratStates();

		/* Manage missiles */
		manageMissiles();

		DoViewUpdate();

   		/* Check if it is the time for schedule real time update (every 10 seconds) */
   		if (getCurrentTime() / RT_INTVL > M->scheduleRTUpdateTimeStamp) {
   			M->scheduleRTUpdateTimeStamp = getCurrentTime() / RT_INTVL;
   			M->scheduleRTUpdate = TRUE;
   			realTimeUpdate();
   		}

		/* Send packets from message queue */
		int time = getCurrentTime ();
		/* If there are messages due to send, dequeue */
		while (queue.minTime () <= time) {
			MSGQueueInstance *queueInst = queue.deQueue();
			MW244BPacket *pack = queueInst->content;
			
			switch (queueInst->type) {
				/* Transient type, no ACK is required */
				case QTYPE_TRANS: {
					sendPacket (pack, queueInst->receiver);
					delete queueInst->content;
					delete queueInst;				
					break;
				}
				case QTYPE_REG: {
					pack->messageID = M->getMessageID ();
					sendPacket (pack, MAX_RATS);	
					/* Push next schedule regular update into message queue */				
					queue.enQueue (new MSGQueueInstance (QTYPE_REG, queueInst->content, MAX_RATS, REG_INTVL));
					delete queueInst;					
					break;
				}
				case QTYPE_ACK: {
					/* Do not send realtime update to those far away; but for every interval of 10 seconds, 
						we send realtime update message to everybody regardless of distance */
					if ((queueInst->content->type == PACKET_RT) && (queueInst->tries == 0))
						if ((!M->rat(queueInst->receiver).nearby) && (!M->scheduleRTUpdate))  
							break;
					
					if (queueInst->tries < MAX_TRIES) {
						sendPacket (pack, queueInst->receiver);					
						MSGQueueInstance *queueInst1 = new MSGQueueInstance (QTYPE_ACK, 
												queueInst->content, queueInst->receiver, QUEUETIMEOUT);
						queueInst1->tries = queueInst->tries + 1;
						queue.enQueue (queueInst1);
						delete queueInst;
					} else if (queueInst->receiver == MAX_RATS) {
						/* The message for all players (Join) expires, do nothing*/
                        delete queueInst->content;
                        delete queueInst;
                    } else {
						/* Lost connection with player. Send IsAlive Query message */
						MW244BPacket *msg = new MW244BPacket;
						msg->messageID = M->getMessageID ();
						msg->receiverIP = M->rat(queueInst->receiver).ratIP;
						msg->receiverPort = M->rat(queueInst->receiver).ratPort;
						msg->type = PACKET_ISALV;
						
						/* Find two neighbors and send out IsAliveQuery messages */
						RatIndexType ratID1(0), ratID2(0);
						getTwoNeighbors(ratID1, ratID2, queueInst->receiver);
						if (ratID1.value() != 0) {
							queue.enQueue (new MSGQueueInstance (QTYPE_ISALVPING, msg, ratID1, -1000));
						} else {
							/* I am the only player in the game, nobody to query */
							ClearRatPosition (queueInst->receiver);	
							UpdateScoreCard (queueInst->receiver);			
						}
						if (ratID2.value() != 0) {
							MW244BPacket *msg2;
							memcpy (msg2, msg, sizeof (MW244BPacket));						
							queue.enQueue (new MSGQueueInstance (QTYPE_ISALVPING, msg2, ratID2, -1000));
						} 
						delete queueInst->content;
						delete queueInst;
					}	
					break;
				}
				case QTYPE_ISALVPING: {
					if (queueInst->tries < MAX_TRIES) {
						int timeout;
						if (queueInst->content->type == PACKET_PING)
							timeout = QUEUETIMEOUT;
						else 
							timeout = ISALVTIMEOUT;	// IsAlive Query message has a different timeout
						sendPacket (pack, queueInst->receiver);
						MSGQueueInstance *queueInst1 = new MSGQueueInstance (QTYPE_ISALVPING, 
												queueInst->content, queueInst->receiver, timeout);
						queueInst1->tries = queueInst->tries + 1;
						queue.enQueue (queueInst1);
						delete queueInst;
					} else if (queueInst->content->type == PACKET_PING) {									
						/* Remove the rat from list */
						ClearRatPosition (queueInst->receiver);	
						UpdateScoreCard (queueInst->receiver);
						
						/* Send IsAlive Dead message */
						MW244BPacket *msg = new MW244BPacket;
						msg->messageID = queueInst->content->messageID;
						msg->type = PACKET_DEAD;
						PIsAliveQuery *packX = (PIsAliveQuery *) &(queueInst->content->body);
						RatIndexType receiver = getRatID (packX->queryIP, packX->queryPort);
						queue.enQueue (new MSGQueueInstance (QTYPE_TRANS, msg, receiver, -1000));
						delete queueInst->content;
						delete queueInst;	
					} else {
						/* No response from IsAlive Query. Probably I got lost from other people */
						delete queueInst->content;
						delete queueInst;
					}	
					break;
				}
				default:
					MWError("Unrecognized queue types\n");						
			}
		}
		M->scheduleRTUpdate = FALSE;		
	}
}

/* Test whether a location is occupied by wall or other rats */
bool
occupied (int tx, int ty) {
	if (M->maze_[tx][ty])
		return TRUE;
		
	if ((MY_X_LOC == tx) && (MY_Y_LOC == ty))
		return TRUE;
		
    RatIndexType ratIndex(0);
	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
		if (ratIndex.value() == MY_RAT_INDEX)
			continue;
		Rat r = M->rat(ratIndex);
		if (r.playing && (r.x.value() == tx) && (r.y.value() == ty))
			return TRUE;
	}
	return FALSE;	
}

/* ----------------------------------------------------------------------- */

static	Direction	_aboutFace[NDIRECTION] ={SOUTH, NORTH, WEST, EAST};
static	Direction	_leftTurn[NDIRECTION] =	{WEST, EAST, NORTH, SOUTH};
static	Direction	_rightTurn[NDIRECTION] ={EAST, WEST, SOUTH, NORTH};

void
aboutFace(void)
{
	M->dirIs(_aboutFace[MY_DIR]);
	updateView = TRUE;
}

/* ----------------------------------------------------------------------- */

void
leftTurn(void)
{
	M->dirIs(_leftTurn[MY_DIR]);
	updateView = TRUE;
}

/* ----------------------------------------------------------------------- */

void
rightTurn(void)
{
	M->dirIs(_rightTurn[MY_DIR]);
	updateView = TRUE;
}

/* ----------------------------------------------------------------------- */

/* remember ... "North" is to the right ... positive X motion */

void
forward(void)
{
	register int	tx = MY_X_LOC;
	register int	ty = MY_Y_LOC;

	switch(MY_DIR) {
	case NORTH:	if (!occupied(tx+1, ty))	tx++; break;
	case SOUTH:	if (!occupied(tx-1, ty))	tx--; break;
	case EAST:	if (!occupied(tx, ty+1))	ty++; break;
	case WEST:	if (!occupied(tx, ty-1))	ty--; break;
	default:
		MWError("bad direction in Forward");
	}
	if ((MY_X_LOC != tx) || (MY_Y_LOC != ty)) {
		M->xlocIs(Loc(tx));
		M->ylocIs(Loc(ty));
		updateView = TRUE;
	}
}

/* ----------------------------------------------------------------------- */

void backward()
{
	register int	tx = MY_X_LOC;
	register int	ty = MY_Y_LOC;

	switch(MY_DIR) {
	case NORTH:	if (!occupied(tx-1, ty))	tx--; break;
	case SOUTH:	if (!occupied(tx+1, ty))	tx++; break;
	case EAST:	if (!occupied(tx, ty-1))	ty--; break;
	case WEST:	if (!occupied(tx, ty+1))	ty++; break;
	default:
		MWError("bad direction in Backward");
	}
	if ((MY_X_LOC != tx) || (MY_Y_LOC != ty)) {
		M->xlocIs(Loc(tx));
		M->ylocIs(Loc(ty));
		updateView = TRUE;
	}
}

/* ----------------------------------------------------------------------- */

void peekLeft()
{
	M->xPeekIs(MY_X_LOC);
	M->yPeekIs(MY_Y_LOC);
	M->dirPeekIs(MY_DIR);

	switch(MY_DIR) {
	case NORTH:	if (!M->maze_[MY_X_LOC+1][MY_Y_LOC]) {
				M->xPeekIs(MY_X_LOC + 1);
				M->dirPeekIs(WEST);
			}
			break;

	case SOUTH:	if (!M->maze_[MY_X_LOC-1][MY_Y_LOC]) {
				M->xPeekIs(MY_X_LOC - 1);
				M->dirPeekIs(EAST);
			}
			break;

	case EAST:	if (!M->maze_[MY_X_LOC][MY_Y_LOC+1]) {
				M->yPeekIs(MY_Y_LOC + 1);
				M->dirPeekIs(NORTH);
			}
			break;

	case WEST:	if (!M->maze_[MY_X_LOC][MY_Y_LOC-1]) {
				M->yPeekIs(MY_Y_LOC - 1);
				M->dirPeekIs(SOUTH);
			}
			break;

	default:
			MWError("bad direction in PeekLeft");
	}

	/* if any change, display the new view without moving! */

	if ((M->xPeek() != MY_X_LOC) || (M->yPeek() != MY_Y_LOC)) {
		M->peekingIs(TRUE);
		updateView = TRUE;
	}
}

/* ----------------------------------------------------------------------- */

void peekRight()
{
	M->xPeekIs(MY_X_LOC);
	M->yPeekIs(MY_Y_LOC);
	M->dirPeekIs(MY_DIR);

	switch(MY_DIR) {
	case NORTH:	if (!M->maze_[MY_X_LOC+1][MY_Y_LOC]) {
				M->xPeekIs(MY_X_LOC + 1);
				M->dirPeekIs(EAST);
			}
			break;

	case SOUTH:	if (!M->maze_[MY_X_LOC-1][MY_Y_LOC]) {
				M->xPeekIs(MY_X_LOC - 1);
				M->dirPeekIs(WEST);
			}
			break;

	case EAST:	if (!M->maze_[MY_X_LOC][MY_Y_LOC+1]) {
				M->yPeekIs(MY_Y_LOC + 1);
				M->dirPeekIs(SOUTH);
			}
			break;

	case WEST:	if (!M->maze_[MY_X_LOC][MY_Y_LOC-1]) {
				M->yPeekIs(MY_Y_LOC - 1);
				M->dirPeekIs(NORTH);
			}
			break;

	default:
			MWError("bad direction in PeekRight");
	}

	/* if any change, display the new view without moving! */

	if ((M->xPeek() != MY_X_LOC) || (M->yPeek() != MY_Y_LOC)) {
		M->peekingIs(TRUE);
		updateView = TRUE;
	}
}

/* ----------------------------------------------------------------------- */

void peekStop()
{
	M->peekingIs(FALSE);
	updateView = TRUE;
}

/* ----------------------------------------------------------------------- */

void shoot()
{	
	/* Update my score */
	M->scoreIs( Score(MY_SCORE - 1) );
	UpdateScoreCard(M->myRatId().value());
	
	/* Send projectile message to all players */	
	MW244BPacket		*pack = new MW244BPacket;
	pack->messageID = M->getMessageID ();
	pack->type = PACKET_PROJ;
	queue.enQueueAll (new MSGQueueInstance(QTYPE_ACK, pack, MAX_RATS, 0));	
	
	/* Unregister previous missile */
	Rat r = M->rat(MY_RAT_INDEX);
	if (r.missile != NULL) {
		r.missile->show(FALSE);
		delete r.missile;
	}
	
	/* Create new missile */
	r.missile = new MissileInstance(M->xloc(), M->yloc(), M->dir());
	M->ratIs(r, MY_RAT_INDEX);
}

/* ----------------------------------------------------------------------- */

/*
 * Exit from game, clean up window
 */

void quit(int sig)
{

	StopWindow();
	exit(0);
}


/* ----------------------------------------------------------------------- */

void NewPosition(MazewarInstance::Ptr m)
{
	Loc newX(0);
	Loc newY(0);
	Direction dir(0); /* start on occupied square */

	while (occupied(newX.value(), newY.value())) {
	  /* MAZE[XY]MAX is a power of 2 */
	  newX = Loc(random() & (MAZEXMAX - 1));
	  newY = Loc(random() & (MAZEYMAX - 1));
	}

	/* prevent a blank wall at first glimpse */

	if (!m->maze_[(newX.value())+1][(newY.value())]) dir = Direction(NORTH);
	if (!m->maze_[(newX.value())-1][(newY.value())]) dir = Direction(SOUTH);
	if (!m->maze_[(newX.value())][(newY.value())+1]) dir = Direction(EAST);
	if (!m->maze_[(newX.value())][(newY.value())-1]) dir = Direction(WEST);

	m->xlocIs(newX);
	m->ylocIs(newY);
	m->dirIs(dir);
}

/* ----------------------------------------------------------------------- */

void MWError(char *s)

{
	StopWindow();
	fprintf(stderr, "CS244BMazeWar: %s\n", s);
	perror("CS244BMazeWar");
	exit(-1);
}

/* ----------------------------------------------------------------------- */

/* Get the rat ID by looking up IP and port information. If the rat ID is not found, 
   allocate an ID to it and return the new ID */
RatIndexType getRatID (unsigned int IP, unsigned int port) {
	/* IP == 0 && Port = 0 means for all player */
	if ((IP == 0) && (port == 0))
		return MAX_RATS;
		
	/* Check if this is my ID */
	if ((IP == MY_IP) && (port == MY_PORT))
		return MY_RAT_INDEX;
	
	/* Lookup Rat in the list */
	RatIndexType ratIndex(0);
	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
		Rat r = M->rat(ratIndex);
		if ((IP == r.ratIP) && (port == r.ratPort)) {
			r.playing = TRUE;
			M->ratIs(r, ratIndex);
			return ratIndex;
		}
	}
	
	/* New Rat found. Add rat to list.  */
	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
		if (ratIndex.value() == MY_RAT_INDEX)
			continue;
		Rat r = M->rat(ratIndex);
		if (r.playing) 
			continue;
			
		r.playing = TRUE;
		r.messageID = 0;	// used for out of order packet detection. 
		r.ratIP = IP;
		r.ratPort = port;
		M->ratIs(r, ratIndex);
		return ratIndex;
	}		
	MWError("Add rat fail. Rat number exceeded maximum allowed value.\n");
	return RatIndexType(MAX_RATS);
}

/* Test whether an rat record exists in the list */
bool testGetRatID (unsigned int IP, unsigned int port) {
	if ((IP == 0) && (port == 0))
		return TRUE;
		
	if ((IP == MY_IP) && (port == MY_PORT))
		return TRUE;
	RatIndexType ratIndex(0);
	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
		Rat r = M->rat(ratIndex);
		if ((r.playing) && (IP == r.ratIP) && (port == r.ratPort))
			return TRUE;
	}
	return FALSE;
}

/* Update Rat information */
void updateRat (RatIndexType ratID, unsigned int xpos, unsigned int ypos, unsigned int dir, int score, char *name, unsigned int messageID) {
	Rat newRat = M->rat(ratID);
	
	/* Make sure the status update packet is not out of order */
	if (messageID < newRat.messageID)
		return;
		
	newRat.messageID = messageID;
	Loc x(xpos);
	Loc y(ypos);
	Direction d(dir);
	Score s(score);
	newRat.setStatus(x, y, d, s);
	strncpy(newRat.name, name, strlen(name));
	newRat.name[strlen(name)] = '\0';
	newRat.playing = TRUE;
	M->ratIs(newRat, ratID);	
	UpdateScoreCard (ratID.value());
}

/* Get rat score */
Score GetRatScore(RatIndexType ratId)
{
  if (ratId.value() == 	M->myRatId().value())
    { return (MY_SCORE); }
  else { return M->rat(ratId).score; }
}

/* ----------------------------------------------------------------------- */

/* Get rat name */
char *GetRatName(RatIndexType ratId)
{
  if (ratId.value() ==	M->myRatId().value())
    { return (MY_NAME); }
  else { return M->rat(ratId).name; }
}

/* Find two active rats. This is used for notifying new player about the rats information 
   in the game, and also used for requesting peers to check whether a player is alive */
void getTwoNeighbors (RatIndexType &ratID1, RatIndexType &ratID2, RatIndexType rat) {
	ratID1 = RatIndexType(MY_RAT_INDEX);
	ratID2 = RatIndexType(MY_RAT_INDEX);
	
	RatIndexType ratIndex(0);
	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
		if ((ratIndex.value() == MY_RAT_INDEX) || (ratIndex.value() == rat.value()))
			continue;
		Rat r = M->rat(ratIndex);
		if (r.playing) {
			if (ratID1 == MY_RAT_INDEX) {
				ratID1 = ratIndex;
				continue;
			}
			ratID2 = ratIndex;
			return;		
		}
	}
}

/* ----------------------------------------------------------------------- */
void ConvertIncoming(MW244BPacket *p)
{
	p->messageID = ntohl(p->messageID);
	p->senderIP = ntohl(p->senderIP);
	p->senderPort = ntohl(p->senderPort);
	p->receiverIP = ntohl(p->receiverIP);
	p->receiverPort = ntohl(p->receiverPort);
	p->type = ntohl(p->type);
	p->hash = ntohl(p->hash);
	
	/* This is identified to be malicious packet.  */	
	if (p->hash != hashFunc(p->messageID, p->senderIP, p->senderPort)) {
		/* Malicious packet detected! */
		delete p;
		p = NULL;
		return;
	}
	switch (p->type) {
		case PACKET_PING: 			  		
	    case PACKET_PONG: 
	    	break;
	    
	    case PACKET_REG: 
        case PACKET_RT: 	
        case PACKET_PROJ: {     	
        	PStatus	*packX = (PStatus *) &(p->body);
        	packX->x = ntohl(packX->x);
        	packX->y = ntohl(packX->y);
        	packX->dir = ntohl(packX->dir);        	
        	packX->score = ntohl(packX->score);
        	break;
		}
        case PACKET_TAGGED: {
        	PTagged *packX = (PTagged *) &(p->body);
        	packX->score = ntohl(packX->score);
        	packX->ownerIP = ntohl(packX->ownerIP);	
        	packX->ownerPort = ntohl(packX->ownerPort);       	
        	break;
        }
        case PACKET_JOIN: {
        	PJoin *packX = (PJoin *) &(p->body);
        	packX->x = ntohl(packX->x);
        	packX->y = ntohl(packX->y);
        	packX->dir = ntohl(packX->dir);
        	packX->score = ntohl(packX->score);
	       	break;
        }
        case PACKET_WELCOME: {
			PWelcome *packX = (PWelcome *) &(p->body);
        	packX->x = ntohl(packX->x);
        	packX->y = ntohl(packX->y);
        	packX->dir = ntohl(packX->dir);
        	packX->score = ntohl(packX->score);
        	packX->IP1 = ntohl(packX->IP1);
        	packX->Port1 = ntohl(packX->Port1);
        	packX->IP2 = ntohl(packX->IP2);
        	packX->Port2 = ntohl(packX->Port2);
        	break;
		}
        case PACKET_ISALV: {
        	PIsAliveQuery *packX = (PIsAliveQuery *) &(p->body);
        	packX->queryIP = ntohl(packX->queryIP);
        	packX->queryPort = ntohl(packX->queryPort);
        	break;
        }
        case PACKET_ALV:    
        case PACKET_DEAD: 
        	break;
        	
        default: 
        	MWError("Unrecognized receive packet format\n");		
	}
}

/* ----------------------------------------------------------------------- */

/* Fill in the packet payload information */
void ConvertOutgoing(MW244BPacket *pack)
{
	switch (pack->type) {
		case PACKET_PING: 			  		
	    case PACKET_PONG: 
	    	break;
	    
	    case PACKET_REG: 
        case PACKET_RT: 	
        case PACKET_PROJ: {     	
        	PStatus	packX;
        	packX.x = htonl(MY_X_LOC);
        	packX.y = htonl(MY_Y_LOC);
        	packX.dir = htonl(MY_DIR);        	
        	packX.score = htonl(MY_SCORE);
        	memcpy (&pack->body, &packX, sizeof(PStatus));
        	break;
		}
        case PACKET_TAGGED: {
        	PTagged packX;
        	packX.score = htonl(MY_SCORE);
        	packX.ownerIP = htonl(pack->receiverIP);	
        	packX.ownerPort = htonl(pack->receiverPort);
			memcpy (&pack->body, &packX, sizeof(PTagged));       	
        	break;
        }
        case PACKET_JOIN: {
        	PJoin packX;
        	packX.x = htonl(MY_X_LOC);
        	packX.y = htonl(MY_Y_LOC);
        	packX.dir = htonl(MY_DIR);
        	packX.score = htonl(MY_SCORE);
        	strcpy(packX.name, MY_NAME);
        	memcpy (&pack->body, &packX, sizeof(PJoin));    		
	       	break;
        }
        case PACKET_WELCOME: {
			PWelcome packX;
			packX.x = htonl(MY_X_LOC);
			packX.y = htonl(MY_Y_LOC);
			packX.dir = htonl(MY_DIR);
			packX.score = htonl(MY_SCORE);
			strcpy(packX.name, MY_NAME);
			
			RatIndexType ratID1(MY_RAT_INDEX), ratID2(MY_RAT_INDEX);
			getTwoNeighbors (ratID1, ratID2, 0);
			if (ratID1 < MAX_RATS) {
				packX.IP1 = htonl(M->rat(ratID1).ratIP);
				packX.Port1 = htonl(M->rat(ratID1).ratPort);
			} else {
				packX.IP1 = htonl(0);	
				packX.Port1 = htonl(0);
			}
			if (ratID2 < MAX_RATS) {
				packX.IP2 = htonl(M->rat(ratID2).ratIP);
				packX.Port2 = htonl(M->rat(ratID2).ratPort);
			} else {
				packX.IP2 = htonl(0);
				packX.Port2 = htonl(0);          
			}
			memcpy (&pack->body, &packX, sizeof(PWelcome));
        	break;
		}
        case PACKET_ISALV: {
        	PIsAliveQuery packX;
        	packX.queryIP = htonl(pack->receiverIP);	
        	packX.queryPort = htonl(pack->receiverPort);
        	memcpy (&pack->body, &packX, sizeof(PIsAliveQuery));
        	break;
        }
        case PACKET_ALV:    
        case PACKET_DEAD: 
        	break;
        	
        default: 
        	MWError("Unrecognized send packet format\n");		
	}
}

/* ----------------------------------------------------------------------- */

int min(int a, int b) { return ((a>b) ? b : a);}

/* Returns true if the rat might be close to me (can see/peek me or I can see/peek it) */
bool computeNearby (Rat r) {
	int xRat = r.x.value();
	int yRat = r.y.value();
	int x = MY_X_LOC;
	int y = MY_Y_LOC;
	int xx, yy;
	
	/* The minimum distance between the rat and my nearby positions */	
	int minDist = MAZEXMAX * MAZEYMAX;	
	
	xx = x; yy = y;
	/* Search along the x aisle that I am in */
	while (!M->maze_[xx][yy]) {
		minDist = min(minDist, M->dist[xx][yy+1][xRat][yRat]);
		minDist = min(minDist, M->dist[xx][yy-1][xRat][yRat]);
		minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
		xx++;
	}
	xx = x; yy = y;
	while (!M->maze_[xx][yy]) {
		minDist = min(minDist, M->dist[xx][yy+1][xRat][yRat]);
		minDist = min(minDist, M->dist[xx][yy-1][xRat][yRat]);
		minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
		xx--;
	}	
	xx = x; yy = y;	
	/* Search along the y aisle that I am in */
	while (!M->maze_[xx][yy]) {
		minDist = min(minDist, M->dist[xx+1][yy][xRat][yRat]);
		minDist = min(minDist, M->dist[xx-1][yy][xRat][yRat]);
		minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
		yy++;
	}
	xx = x; yy = y;
	while (!M->maze_[xx][yy]) {
		minDist = min(minDist, M->dist[xx+1][yy][xRat][yRat]);
		minDist = min(minDist, M->dist[xx-1][yy][xRat][yRat]);
		minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
		yy--;
	}

	/* Search along the aisle that I can peek */
	if (!M->maze_[x+1][y]) {
		xx = x+1; yy = y;
		while (!M->maze_[xx][yy]) {
			minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
			yy++;
		}
		xx = x+1; yy = y;
		while (!M->maze_[xx][yy]) {
			minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
			yy--;
		}		
	}	
	
	/* Search along the aisle that I can peek */
	if (!M->maze_[x-1][y]) {
		xx = x-1; yy = y;
		while (!M->maze_[xx][yy]) {
			minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
			yy++;
		}
		xx = x-1; yy = y;
		while (!M->maze_[xx][yy]) {
			minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
			yy--;
		}		
	}	
	
	/* Search along the aisle that I can peek */
	if (!M->maze_[x][y+1]) {
		xx = x; yy = y+1;
		while (!M->maze_[xx][yy]) {
			minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
			xx++;
		}
		xx = x; yy = y+1;
		while (!M->maze_[xx][yy]) {
			minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
			xx--;
		}		
	}	
	
	/* Search along the aisle that I can peek */
	if (!M->maze_[x][y-1]) {
		xx = x; yy = y-1;
		while (!M->maze_[xx][yy]) {
			minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
			xx++;
		}
		xx = x; yy = y-1;
		while (!M->maze_[xx][yy]) {
			minDist = min(minDist, M->dist[xx][yy][xRat][yRat]);
			xx--;
		}		
	}		
	
	/* If the nearby position is reachable since last status update time, return true */
	if ((getCurrentTime() - r.time) * MAX_RAT_V >= minDist) 
		return TRUE;
	else 
		return FALSE;
}


/* This is used to extrapolate status of the rats, and display. Determine 
   whether the rat is close enough */
void ratStates()
{
	RatIndexType ratIndex(0);
	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
		if (ratIndex == MY_RAT_INDEX)
			continue;
		Rat r = M->rat(ratIndex);
		if (!r.playing) 
			continue;
		
		/* Set rat position */
		SetRatPosition(ratIndex, r.x, r.y, r.dir);
		
		/* Determine whether the rat is nearby */
		r.nearby = computeNearby (r);
		M->ratIs(r, ratIndex);
	}


	
	updateView = TRUE;
}

/* ----------------------------------------------------------------------- */

/* Manage the missile. Determine whether a missile hits me or expires */
void manageMissiles()
{
	RatIndexType ratIndex(0);
	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
		Rat r = M->rat(ratIndex);
  		if (!r.playing || (r.missile == NULL)) 
  			continue;
  			
		/* Check whether missile expires */  		
  		if (r.missile->expire()) {
  			r.missile->show(FALSE);
  			delete r.missile;
  			r.missile = NULL;
  			M->ratIs(r, ratIndex);
  		} else {
  			/* Check whether missile hits me */
  			if ((ratIndex != MY_RAT_INDEX) && (r.missile->hit())) {
  				M->scoreIs(MY_SCORE - 5);  	
  				r.score = r.score.value() + 11;
  				
  				/* Send the Tagged message */
				MW244BPacket *pack = new MW244BPacket;
				pack->messageID = M->getMessageID ();
				pack->receiverIP = r.ratIP;	
				pack->receiverPort = r.ratPort;
				pack->type = PACKET_TAGGED;
				queue.enQueueAll (new MSGQueueInstance(QTYPE_ACK, pack, MAX_RATS, 0));
				
				/* Delete the missile */
				r.missile->show(FALSE);
				delete r.missile;
				r.missile = NULL;
	  			M->ratIs(r, ratIndex);
	  			
	  			/* Update score card */
	  			UpdateScoreCard(ratIndex.value());	
  				UpdateScoreCard(MY_RAT_INDEX);
				
				/* Respawn */
				NewPosition(M);					
				break;
  			} else {
  				/* Show the missile on map*/
  				r.missile->show(TRUE);
  			}
  			
  		}
  	}
}
/* ----------------------------------------------------------------------- */

void DoViewUpdate()
{
	if (updateView) {	/* paint the screen */
		ShowPosition(MY_X_LOC, MY_Y_LOC, MY_DIR);
		if (M->peeking())
			ShowView(M->xPeek(), M->yPeek(), M->dirPeek());
		else
			ShowView(MY_X_LOC, MY_Y_LOC, MY_DIR);
		updateView = FALSE;
	}
}

/* ----------------------------------------------------------------------- */

/* Send packet */
void sendPacket(MW244BPacket *p, RatIndexType receiver)
{
	/* For simulating packet loss */
	if (rand() % 100 < 5)
		return; 

	MW244BPacket *pack = new MW244BPacket;
	memcpy(pack, p, sizeof(MW244BPacket));
	
	ConvertOutgoing(pack);
	pack->hash = htonl(hashFunc(pack->messageID, MY_IP, MY_PORT));	
	pack->messageID = htonl(pack->messageID);
	pack->senderIP = htonl(MY_IP);
	pack->senderPort = htonl(MY_PORT);
	pack->type = htonl(pack->type);	

	if (receiver.value() == MAX_RATS) {
		pack->receiverIP = htonl(0);
		pack->receiverPort = htonl(0);
	} else {
		pack->receiverIP = htonl(M->rat(receiver).ratIP);
		pack->receiverPort = htonl(M->rat(receiver).ratPort);
	}

	if (sendto((int)M->theSocket(), pack, sizeof(*pack), 0,
		   (struct sockaddr*) &groupAddr, sizeof(Sockaddr)) < 0)
	  { MWError("Send packet Error"); }
}

/* ----------------------------------------------------------------------- */

/* Process incoming packet */
void processPacket (MWEvent *eventPacket)
{
	MW244BPacket		*pack = eventPacket->eventDetail;
	if (pack == NULL)
		return;
	
	/* Get sender and receiver */
	RatIndexType sender = getRatID(pack->senderIP, pack->senderPort);
	RatIndexType receiver = getRatID(pack->receiverIP, pack->receiverPort);
	
	/* If this is not my packet, ignore it */
	if ((receiver != MY_RAT_INDEX) && ((pack->receiverIP) || (pack->receiverPort)))
		return;		
	
	MW244BPacket		*reply = new MW244BPacket;
	reply->messageID = M->getMessageID ();
	
	/* Send ACK message to those require one */
	if ( (pack->type == PACKET_PING) || (pack->type == PACKET_RT) 
		|| (pack->type == PACKET_PROJ) || (pack->type == PACKET_TAGGED) 
		|| (pack->type == PACKET_WELCOME) ) {
			reply->messageID = pack->messageID;
			reply->type = PACKET_PONG;
			queue.enQueue (new MSGQueueInstance(QTYPE_TRANS, reply, sender, 0));	  		
		}
	
	switch (pack->type) {
		case PACKET_PING: 	
	  		break;
	  		
        case PACKET_PONG: {
			/* Delete the message in the queue that is waiting for this reply */		
			MSGQueueInstance *queueInst = queue.deQueue (pack->messageID, sender);
			if (queueInst == NULL)
				break;
				
			/* If the msg is a IsAlive Ping, reply IsAliveQuery Hit (alive) */
			if (queueInst->type == QTYPE_ISALVPING) {
				reply->messageID = queueInst->content->messageID;
				reply->type = PACKET_ALV;				
				PIsAliveQuery *packX = (PIsAliveQuery *) &(queueInst->content->body);
				receiver = getRatID(packX->queryIP, packX->queryPort);
				queue.enQueue (new MSGQueueInstance (QTYPE_TRANS, reply, receiver, 0));		
			} else {
				delete reply;
			}
			delete queueInst->content;
			delete queueInst;
        	break;
        }
        /* Regular and real time status update */
        case PACKET_REG: 
        case PACKET_RT: {		
        	PStatus	*packX = (PStatus *) &(pack->body);
			updateRat(sender, packX->x, packX->y, packX->dir, packX->score, M->rat(sender).name, pack->messageID);
        	break;
        }
        case PACKET_PROJ: {
        	PStatus	*packX = (PStatus *) &(pack->body);
			updateRat(sender, packX->x, packX->y, packX->dir, packX->score, M->rat(sender).name, pack->messageID);        	

			/* Unregister previous missile */
       		Rat r = M->rat(sender);
       		if (r.missile != NULL) {
       			r.missile->show(FALSE);
       			delete r.missile;
       		}
       		
        	/* Register the missile */       		
       		r.missile = new MissileInstance(Loc(packX->x), Loc(packX->y), Direction(packX->dir));
        	M->ratIs(r, sender);       		
        	break;
		}
        case PACKET_TAGGED: {
        	PTagged *packX = (PTagged *) &(pack->body);
        	RatIndexType owner = getRatID(packX->ownerIP, packX->ownerPort);        	
			
			/* Unregister the missile and update projectile's owner's score */
        	Rat r2 = M->rat(owner);
        	r2.score = r2.score.value() + 11;
        	if (r2.missile) {
        		r2.missile->show(FALSE);
        		delete r2.missile;
        	}
        	r2.missile = NULL;        	
        	M->ratIs(r2, owner);
        	if (owner == MY_RAT_INDEX)
        		M->scoreIs(Score(MY_SCORE+11));
        	
        	/* Update victim's score */
        	Rat r = M->rat(sender);
        	r.score = packX->score;
        	r.respawn();
        	M->ratIs(r, sender);   		
        	UpdateScoreCard(sender.value());
        	UpdateScoreCard(owner.value());
        	break;
        }
        case PACKET_JOIN: {
        	/* Update status */
        	PJoin *packX = (PJoin *) &(pack->body);
        	updateRat(sender, packX->x, packX->y, packX->dir, packX->score, packX->name, M->rat(sender).messageID);
        	UpdateScoreCard(sender.value());
        	
        	/* Send welcome message */
        	reply->messageID = pack->messageID;
			reply->type = PACKET_WELCOME;
			queue.enQueue (new MSGQueueInstance(QTYPE_ACK, reply, sender, 0));
        	break;
        }	
        case PACKET_WELCOME: {
        	/* Update status */
        	PWelcome *packX = (PWelcome *) &(pack->body);
        	updateRat(sender, packX->x, packX->y, packX->dir, packX->score, packX->name, M->rat(sender).messageID);
        	UpdateScoreCard(sender.value());
        	
        	/* Remove the Join message in message queue waiting for this reply */
			MSGQueueInstance *queueInst = queue.deQueue (pack->messageID, sender);
			if (queueInst == NULL)
				break;

			/* Add two neighbor rats into list, and wait for their ACK */
			if ((packX->IP1 != 0) || (packX->Port1 != 0)) {
				bool exist = testGetRatID(packX->IP1, packX->Port1);
				RatIndexType ratID1 = getRatID(packX->IP1, packX->Port1);
				if (!exist) {
					MSGQueueInstance *queueInst1 = new MSGQueueInstance (*queueInst);
					queueInst1->receiver = ratID1;
					queue.enQueue (queueInst1);
				}
			}
			if ((packX->IP2 != 0) || (packX->Port2 != 0)) {
				bool exist = testGetRatID(packX->IP1, packX->Port1);			
				RatIndexType ratID2 = getRatID(packX->IP2, packX->Port2);
				if (!exist) {
					MSGQueueInstance *queueInst2 = new MSGQueueInstance (*queueInst);
					queueInst2->receiver = ratID2;
					queue.enQueue (queueInst2);
				}
			}
			delete queueInst->content;
			delete queueInst;        	     
        	break;
		}
        case PACKET_ISALV: {
        	PIsAliveQuery *packX = (PIsAliveQuery *) &(pack->body);
        	/* If the player in query is not in my list, send IsAlive Hit (Dead) message */
        	if (!testGetRatID (packX->queryIP, packX->queryPort)) {
				MW244BPacket *msg = new MW244BPacket;
				msg->messageID = pack->messageID;
				msg->type = PACKET_DEAD;
				queue.enQueue (new MSGQueueInstance (QTYPE_TRANS, msg, sender, 0));        		
				break;
        	}

        	/* If the rat in query is me, send Query Hit (alive) message */        	
        	RatIndexType queryRat = getRatID (packX->queryIP, packX->queryPort);
        	if (queryRat == MY_RAT_INDEX) {
        		reply->messageID = pack->messageID;
        		reply->type = PACKET_ALV;
        		queue.enQueue (new MSGQueueInstance (QTYPE_TRANS, reply, sender, 0));
        		break;
        	}
        	
        	/* Send a IsAlivePing message to query rat*/
        	reply->messageID = pack->messageID;
			reply->type = PACKET_PING;
			receiver = getRatID(packX->queryIP, packX->queryPort);
			
			packX->queryIP = pack->senderIP;
			packX->queryPort = pack->senderPort;
			memcpy (&(reply->body), packX, sizeof (PIsAliveQuery));
			queue.enQueue (new MSGQueueInstance(QTYPE_ISALVPING, reply, queryRat, 0));
        	break;
        }
        case PACKET_ALV: {
        	/* The rat is alive. Delete all pending IsAliveQuery regarding this rat */
        	RatIndexType ratIndex(0);
        	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
	        	MSGQueueInstance *queueInst = queue.deQueue (pack->messageID, ratIndex);	
    	    	if (queueInst != NULL) {
        			delete queueInst->content;
        			delete queueInst;
	        	}        	
	        }
    	   	break;
        }
        case PACKET_DEAD: {
        	/* Remove IsAlive Query message from message queue; Label player as dead */
        	MSGQueueInstance *queueInst = queue.deQueue (pack->messageID, sender);
        	if (queueInst == NULL) 
        		break;
			RatIndexType deadRat = getRatID (queueInst->content->receiverIP, queueInst->content->receiverPort);
			ClearRatPosition (deadRat);	
			UpdateScoreCard (deadRat);
       		delete queueInst->content;
       		delete queueInst;
       		
       		/* Clean up other pending IsAliveQuery messages */
          	RatIndexType ratIndex(0);
        	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
	        	MSGQueueInstance *queueInst = queue.deQueue (pack->messageID, ratIndex);	
    	    	if (queueInst != NULL) {
        			delete queueInst->content;
        			delete queueInst;
	        	}        	
	        }     		
        	break;
        }
        default:
        	MWError("Unrecognized packet format\n");
	}
}

/* ----------------------------------------------------------------------- */


void
netInit()
{
	Sockaddr	nullAddr;
	Sockaddr	*thisHost;
	char		buf[128];
	int		reuse;
	u_char          ttl;
	struct ip_mreq  mreq;

	/* MAZEPORT will be assigned by the TA to each team */
	M->mazePortIs(htons(MAZEPORT));

	gethostname(buf, sizeof(buf));	
	if ((thisHost = resolveHost(buf)) == (Sockaddr *) NULL)
	  MWError("who am I?");
	bcopy((caddr_t) thisHost, (caddr_t) (M->myAddr()), sizeof(Sockaddr));
	
	/* Record my Ip address and port number */
	M->myIPIs(thisHost->sin_addr.s_addr);
	M->myPortIs(thisHost->sin_port);

	M->theSocketIs(socket(AF_INET, SOCK_DGRAM, 0));
	if (M->theSocket() < 0)
	  MWError("can't get socket");

	/* SO_REUSEADDR allows more than one binding to the same
	   socket - you cannot have more than one player on one
	   machine without this */
	reuse = 1;
	if (setsockopt(M->theSocket(), SOL_SOCKET, SO_REUSEADDR, &reuse,
		   sizeof(reuse)) < 0) {
		MWError("setsockopt failed (SO_REUSEADDR)");
	}

	nullAddr.sin_family = AF_INET;
	nullAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	nullAddr.sin_port = M->mazePort();
	if (bind(M->theSocket(), (struct sockaddr *)&nullAddr,
		 sizeof(nullAddr)) < 0)
	  MWError("netInit binding");

	/* Multicast TTL:
	   0 restricted to the same host
	   1 restricted to the same subnet
	   32 restricted to the same site
	   64 restricted to the same region
	   128 restricted to the same continent
	   255 unrestricted

	   DO NOT use a value > 32. If possible, use a value of 1 when
	   testing.
	*/

	ttl = 32;
	if (setsockopt(M->theSocket(), IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
		   sizeof(ttl)) < 0) {
		MWError("setsockopt failed (IP_MULTICAST_TTL)");
	}

	  u_char loop = 0;
	  if (setsockopt(M->theSocket(), IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
	             sizeof(loop)) < 0) {
		MWError("setsockopt failed (IP_MULTICAST_LOOP)");
	  }

	/* join the multicast group */
	mreq.imr_multiaddr.s_addr = htonl(MAZEGROUP);
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);
	if (setsockopt(M->theSocket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)
		   &mreq, sizeof(mreq)) < 0) {
		MWError("setsockopt failed (IP_ADD_MEMBERSHIP)");
	}
        
    /* Send out Join Game message */
 	MW244BPacket		*pack = new MW244BPacket;
	pack->messageID = M->getMessageID ();
	pack->type = PACKET_JOIN;
	MSGQueueInstance *queueInst = new MSGQueueInstance(QTYPE_ACK, pack, MAX_RATS, 0);
	queue.enQueue (queueInst);	  	
	
	printf("\n");
	
	M->myRatIdIs(0);
	M->scoreIs(0);
	SetMyRatIndexType(0);

	/* Get the multi-cast address ready to use in SendData()
           calls. */
	memcpy(&groupAddr, &nullAddr, sizeof(Sockaddr));
	groupAddr.sin_addr.s_addr = htonl(MAZEGROUP);

}

int getCurrentTime () {
	struct timeval timenow;
	gettimeofday(&timenow, 0);
	return (timenow.tv_sec * 1000 + timenow.tv_usec / 1000 - TimeZero);

}

/* ----------------------------------------------------------------------- */

/* Set rat status */
void Rat::setStatus(Loc _x, Loc _y, Direction _dir, Score _score) {
	playing = TRUE;	
	x = _x;
	y = _y;
	dir = _dir;
	score = _score;
	time = getCurrentTime ();	// Update timestamp
}


/* When a rat is respawning, the status is set to 0 so the rat will 
	not be visible to others; the score will not change */
void Rat::respawn() {
	Loc _x(0);
	Loc _y(0);
	Direction _dir(0);
	setStatus(_x, _y, _dir, score);
	nearby = FALSE;
	playing = TRUE;
}

/* ------------------------------------------------------*/

/* Return the minimum timestamp of the queue */
int MSGQueue::minTime() {
	if (head == NULL) 
		MWError("Empty Message Queue! \n");
		
	return head->time;
}

/* Add a new message into the queue, the queue is in time order */
void MSGQueue::enQueue(MSGQueueInstance *instance) {
	if (head == NULL)
		head = instance;
	else if (instance->time <= head->time)  {
		instance->next = head;
		head = instance;
	} else {
		MSGQueueInstance *p = head;
		while ((p->next != NULL) && (p->next->time < instance->time)) {
			p = p->next;
		}	
			
		/* Deduplication */
		if ((p->content->messageID == instance->content->messageID) && (p->receiver == instance->receiver) 
				&& (p->content->messageID > 0)) {
			delete instance->content;
			delete instance;
			return;
		}
		
		instance->next = p->next;
		p->next = instance;		
	}
}

/* Extract the message with minimum time stamp */
MSGQueueInstance *MSGQueue::deQueue() {
	if (head == NULL)
		MWError("Queue is empty. Cannot dequeue.");
		
	MSGQueueInstance *p = head;
	head = head->next;

	return p;
}


/* Extract the message with certain messageID, used for ACK messages */
MSGQueueInstance *MSGQueue::deQueue ( unsigned int messageID, RatIndexType receiver ) {
	MSGQueueInstance *p = head;
	MSGQueueInstance *q = NULL;
	while (p != NULL) {
		if ((p->content->messageID == messageID) && (p->type != QTYPE_TRANS)) {
			if ((p->receiver != MAX_RATS) && (p->receiver != receiver))	{
				q = p;
				p = p->next; 
				continue;					
			}
			if (p->receiver != MAX_RATS) {
				if (q == NULL)
					head = p->next;
				else 
					q->next = p->next;
				return p;
			}
			/* If the message is waiting for ACK for all players, then retain the message 
				in the queue and return a copy*/
			return (new MSGQueueInstance(*p));
		}
		q = p;
		p = p->next;
	}
	
	/* Dequeue fails */
	return NULL;
}	

/* Cleanup message queue upon exit */
void MSGQueue::freeQueue () {
	MSGQueueInstance *p = head;
	MSGQueueInstance *q = NULL;
	while (p != NULL) {
		q = p;
		p = p->next;
		delete q->content;
		delete q;
	}
}

/* ----------------------------------------------------------- */

/* Check whether the missile expires */
bool MissileInstance::expire() {
	int t = getCurrentTime ();
	return (t > time + ttl);			
}
	
/* Check whether I am hit by the missile */
bool MissileInstance::hit () {		
	int t = getCurrentTime ();
	
	int tx = x.value() + round(dx * (t-time) * VPROJ);
	int ty = y.value() + round(dy * (t-time) * VPROJ);
	
	if ((tx == MY_X_LOC) && (ty == MY_Y_LOC))
		return TRUE;
	else 
		return FALSE;		
}

/* Show missile on map */
void MissileInstance::show (bool show) {		
	int timedif = getCurrentTime() - time;
	
	int xx = round(x.value() + timedif * VPROJ * dx);
	int yy = round(y.value() + timedif * VPROJ * dy);
	int lastx = xx - dx;
	int lasty = yy - dy;
	if (M->maze_[lastx][lasty]) {
		lastx = xx;
		lasty = yy;
	}
	if (!M->maze_[xx][yy])
		showMissile(xx, yy, dir, lastx, lasty, true);
	else 
		clearSquare(lastx, lasty);
	
	if ((!show) && (!M->maze_[xx][yy])) {
		clearSquare(xx,yy);
		clearSquare(lastx, lasty);
	}
}

/* Add a broadcast message into the queue by manually euqueue messages to all players */
void MSGQueue::enQueueAll (MSGQueueInstance *queueInst) {
	RatIndexType ratIndex(0);
	for (ratIndex = 0; ratIndex < MAX_RATS; ratIndex = RatIndexType(ratIndex.value() + 1)) {
		if (ratIndex.value() == MY_RAT_INDEX)
			continue;
		Rat r = M->rat(ratIndex);
		if (!r.playing) 
			continue;
		MSGQueueInstance *queueInst1 = new MSGQueueInstance (*queueInst);
		queueInst1->receiver = ratIndex;
		queue.enQueue(queueInst1);
	}
	delete queueInst;
}

/* Security hash function */
unsigned int hashFunc(unsigned int a, unsigned int b, unsigned int c) {
	return a^b^c;
}
